/*-----------------------------------------------------------------------------
 * Copyright (c) <2013-2015>, <Huawei Technologies Co., Ltd>
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation and/
 * or other materials provided with the distribution.
 * 3. Neither the name of the copyright holder nor the names of its contributors
 * may be used to endorse or promote products derived from this software without
 * specific prior written permission.
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 ------------------------------------------------------------------------------
 * Notice of Export Control Law
 ==============================================
 * Huawei LiteOS may be subject to applicable export control laws and regulations,
 * which might include those applicable to Huawei LiteOS of U.S. and the country in
 * which you are located.
 * Import, export and usage of Huawei LiteOS in any manner by you shall be in
 * compliance with such applicable export control laws and regulations.
 *---------------------------------------------------------------------------*/


#include "ff.h"

#define HAVE_DIR_STRUCTURE

#include "fatfs.h"
#ifdef LOSCFG_FS_FAT

#include "dirop_fat.h"
#include "errcode_fat.h"
#include "disk.h"
#include "errno.h"

#include "los_tables.h"


#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif /* __cplusplus */
#endif /* __cplusplus */

#ifdef LOSCFG_FS_FAT_VIRTUAL_PARTITION
#include "virpart.h"
#include "virpartff.h"

FAT_VIR_PART    FatVirPart = {0};

#endif


PARTITION VolToPart[_VOLUMES];
static struct inode *disk_dev[_VOLUMES];

#ifdef LOSCFG_FS_FAT_CACHE
extern int sd_sync(int);
#endif


/* Returns -1 if there is no space to install fatfs driver, else returns >=0 */
static int fatfs_get_disk(struct inode *node)
{
    int index;

    for (index = 0; index < _VOLUMES; index ++)
    {
        if (disk_dev[index] == node)
            return index;
    }

    return -1;
}

extern int fatfs_get_vol(FATFS *fat);


int fatfs_2_vfs(int result)
{
    int status = ENOERR;

#ifdef LOSCFG_FS_FAT_VIRTUAL_PARTITION
    if(result < 0 || result >= VIRERR_BASE)
        return result;
#else
    if(result < 0)
        return result;
#endif


    /* FatFs errno to Libc errno */
    switch (result)
    {
        case FR_OK:
            break;

        case FR_NO_FILE:
        case FR_NO_PATH:
        case FR_NO_FILESYSTEM:
            status = -ENOENT;
            break;

        case FR_INVALID_NAME:
            status = -EINVAL;
            break;

        case FR_EXIST:
        case FR_INVALID_OBJECT:
            status = -EEXIST;
            break;

        case FR_DISK_ERR:
        case FR_NOT_READY:
        case FR_INT_ERR:
            status = -EIO;
            break;

        case FR_WRITE_PROTECTED:
            status = -EROFS;
            break;
        case FR_MKFS_ABORTED:
        case FR_INVALID_PARAMETER:
            status = -EINVAL;
            break;

        case FR_NO_SPACE_LEFT:
            status = -ENOSPC;
            break;
        case FR_NO_DIRENTRY:
            status = -ENFILE;
            break;
        case FR_NO_EMPTY_DIR:
            status = -ENOTEMPTY;
            break;
        case FR_IS_DIR:
            status = -EISDIR;
            break;
        case FR_NO_DIR:
            status = -ENOTDIR;
            break;
        case FR_NO_EPERM:
        case FR_DENIED:
            status = -EPERM;
            break;
        case FR_LOCKED:
            status = -EBUSY;
            break;
#ifdef LOSCFG_FS_FAT_VIRTUAL_PARTITION
        case FR_MODIFIED:
            status = VIRERR_MODIFIED;
            break;
        case FR_CHAIN_ERR:
            status = VIRERR_CHAIN_ERR;
            break;
        case FR_OCCUPIED:
            status = VIRERR_OCCUPIED;
            break;
        case FR_NOTCLEAR:
            status = VIRERR_NOTCLEAR;
            break;
        case FR_NOTFIT:
            status = VIRERR_NOTFIT;
            break;
        case FR_INVAILD_FATFS:
            status = VIRERR_INTER_ERR;
            break;
#endif
        default:
            status = -1;
            break;
    }

    return status;
}

int fatfs_bind(struct inode *blkdriver, const void *data, void **handle, const char *relpath)
{
    FATFS    *fat = (FATFS *)NULL;
    int      result;
    BYTE      index;
    int       err = ENOERR;
    los_part *part = (los_part *)NULL;
    char drive[8];

    if (!blkdriver || !blkdriver->u.i_bops)
        return -ENODEV;

    if (blkdriver->u.i_bops->open && blkdriver->u.i_bops->open(blkdriver) != 0)
        return -ENODEV;

    part = los_part_find(blkdriver);
    if (part == NULL)
       return -ENODEV;

    if (part->part_name != NULL)
        return -EACCES;

    if (!_MULTI_PARTITION && part->part_no_mbr > 1) /*lint !e506*/
        return -EPERM;

    err = los_part_cpyName(part, relpath);
    if (err != 0)
    {
        return -ENOMEM;
    }

    index = (BYTE) fatfs_get_disk((struct inode *)NULL);
    if (index >= _VOLUMES || (INT8)index < 0)
    {
        err = -EBUSY;
        goto __errout;
    }
    VolToPart[index].di = part->disk_id;
    VolToPart[index].pd = part->part_id;
    VolToPart[index].pt = part->part_no_mbr;
    VolToPart[index].ps = part->sector_start;
    VolToPart[index].pc = part->sector_count;

    fat = (FATFS *)zalloc(sizeof(FATFS));
    if (fat == NULL)
    {
        err = -ENOMEM;
        goto __errout_with_disk;
    }
    result = snprintf_s(drive, sizeof(drive), sizeof(drive) - 1, "%d:/", index);
    if (result < 0)
    {
        err = -EINVAL;
        free(fat);
        goto __errout_with_disk;
    }

    /* save blockdriver inode : driver will use in f_mount*/
    disk_dev[index] = blkdriver;
    /* mount fatfs, mmc always 0 logic driver */
    result = f_mount(fat, drive, 1);
    if (result == FR_OK)
    {
        DIR *dir = (DIR *)NULL;

        dir = (DIR *)malloc(sizeof(DIR));
        if (dir == NULL)
        {
            err = -ENOMEM;
            goto __errout_with_mount;
        }

        /* open the root directory, returns error if fatfs is invalid */
        result = f_opendir(dir, drive);
        if (result != FR_OK)
        {
            err = fatfs_2_vfs(result);
            free(dir);
            goto __errout_with_mount;
        }
        result = f_closedir(dir);
        if (result != FR_OK)
        {
            err = fatfs_2_vfs(result);
            free(dir);
            goto __errout_with_mount;
        }

        /* mount ok! */
        *handle = (void*)fat;
        free(dir);
#ifdef LOSCFG_FS_FAT_VIRTUAL_PARTITION
    /* Parent Partition Mount Success, then process virtual partition mount operation */
        if(result == FR_OK)
        {
            if (fat->fs_type != FS_FAT32)
                return VIRERR_NOTFIT;
            if (blkdriver != inode_find(FatVirPart.VirPartInfo.devpartpath, (const char **)NULL))
            {
                return VIRERR_NOPARAM;
            }
            else
            {
                result = fatfs_bind_vir_part(fat,index);
                if (result != FR_OK && result < VIRERR_BASE)
                {
                    result = VIRERR_INTER_ERR;
                }
            }
        }
#endif
        return fatfs_2_vfs(result);
    }
    else
    {
        err = fatfs_2_vfs(result);
    }
__errout_with_mount:
    (void)f_mount((FATFS *)NULL, drive, 0);
    if(fat->win != NULL)
        free((void *)fat->win);
    free((void *)fat);

__errout_with_disk:
    disk_dev[index] = (struct inode *)NULL;

__errout:
    free(part->part_name);
    part->part_name = NULL;

    return err;
}

int fatfs_unbind(void *handle, struct inode **blkdriver)
{
    BYTE    index;
    FATFS  *fat = (FATFS *)NULL;
    int     result;
    los_part *part = (los_part *)NULL;
    char drive[8];

    fat = (FATFS *)handle;
    if (fat == NULL)
        return -EINVAL;
    index = (BYTE) fatfs_get_vol(fat);
    if (index >= _VOLUMES || (INT8)index < 0)
        return -EINVAL;
    result = snprintf_s(drive, sizeof(drive), sizeof(drive) - 1, "%d:/", index);
    if (result < 0)
    {
        return -EINVAL;
    }

#ifdef LOSCFG_FS_FAT_VIRTUAL_PARTITION
    /* Release virtual partition objects before unbind real partition */
    result = fatfs_unbind_vir_part(fat);
    if (result == FR_OK)
    {
        part = los_part_find(disk_dev[index]);
        if (part == NULL || part->dev == inode_find(FatVirPart.VirPartInfo.devpartpath, (const char **)NULL))
        {
            FatVirPart.ParamSet = FALSE;
            (void)memset_s(DevPartName, sizeof(DevPartName), 0, sizeof(DevPartName));
            (void)memset_s(&FatVirPart.VirPartInfo, sizeof(virpartinfo), 0, sizeof(virpartinfo));
        }
    }
    else
      return fatfs_2_vfs(result);
#endif
    result = f_mount((FATFS *)NULL, drive, 0);
    if (result != FR_OK)
      return fatfs_2_vfs(result);

    VolToPart[index].di = 0;
    VolToPart[index].pd = 0;
    VolToPart[index].pt = 0;
    VolToPart[index].ps = 0;
    VolToPart[index].pc = 0;

    part = los_part_find(disk_dev[index]);
    if (part != NULL && part->part_name)
    {
        free(part->part_name);
        part->part_name = NULL;
    }

    if (blkdriver != NULL)
        *blkdriver = disk_dev[index];
    disk_dev[index] = (struct inode *)NULL;
    if(fat->win != NULL)
        free((void *)fat->win);
    free((void *)fat);

    return OK;
}

int fatfs_open(struct file *filep, const char *relpath, int oflags, mode_t mode)
{
    BYTE      f_mode;
    char     *f_path = (char *)NULL;
    FATFS    *fat = (FATFS *)NULL;
    FIL      *fp = (FIL *)NULL;
    FRESULT   result;
    int       vol;
    los_part *part = (los_part *)NULL;
    int      ret;

    if (!filep || !filep->f_inode || !filep->f_inode->i_private || !relpath)
        return -EINVAL;

    if (oflags & O_DIRECTORY)
        return -EACCES;

    if (strlen(relpath) > MAX_LFNAME_LENTH - VOLUME_CHAR_LENGTH)
        return -ENAMETOOLONG;

    if(filep->f_inode->mountflags == MS_RDONLY && oflags!= O_RDONLY)
    {
        return -EACCES;
    }
    fat = (FATFS *)filep->f_inode->i_private;

    vol = fatfs_get_vol(fat);
    if (vol < 0 || vol > _VOLUMES)
        return -ENOENT;

    f_path = (char *)malloc(MAX_LFNAME_LENTH);
    if (f_path == NULL)
        return -ENOMEM;

    ret = snprintf_s(f_path, MAX_LFNAME_LENTH, MAX_LFNAME_LENTH - 1, "%d:%s", vol, relpath);
    if (ret < 0)
    {
        free(f_path);
        return -EINVAL;
    }

    f_mode = FA_READ;

    if (oflags & O_WRONLY)
        f_mode |= FA_WRITE;
    if ((oflags & O_ACCMODE) & O_RDWR)
        f_mode |= FA_WRITE;
    /* Creates a new file if the file is not existing, otherwise, just open it. */
    if (oflags & O_CREAT)
    {
        f_mode |= FA_OPEN_ALWAYS;
        /* Creates a new file. If the file already exists, the function shall fail. */
        if (oflags & O_EXCL)
        {
            f_mode |= FA_CREATE_NEW;
        }
    }
    /* Creates a new file. If the file already exists, its length shall be truncated to 0. */
    if (oflags & O_TRUNC)
        f_mode |= FA_CREATE_ALWAYS;

    fp = (FIL *)malloc(sizeof(FIL));
    if (fp == NULL)
    {
        free((void *)f_path);
        return -ENOMEM;
    }
    fp->buf = NULL;
    // 0:loop/testfile0
    result = f_open(fp, f_path, f_mode);
    free((void *)f_path);
    if (result == FR_OK)
    {
        filep->f_pos  = fp->fptr;
        filep->f_priv = fp;

        if (oflags & O_APPEND)
        {
            /* seek to the end of file */
            (void)f_lseek(fp, fp->obj.objsize);
            filep->f_pos = fp->fptr;
        }
    }
    else
    {
        /* open failed, release memory and return errno*/
        if(fp->buf != NULL)
            free((void *)fp->buf);
        free((void *)fp);
        return fatfs_2_vfs(result);
    }

#ifdef LOSCFG_FS_FAT_CACHE
    if (filep->f_inode->mountflags != MS_NOSYNC && result == FR_OK)
    {
        part = get_part((int)fat->drv);
        if (part != NULL)
        {
            (void)sd_sync(part->disk_id);
        }
        else
            return -ENODEV;
    }
#endif

    return OK;
}

int fatfs_close(struct file *filep)
{
    FATFS    *fat = (FATFS *)NULL;
    FIL      *fp = (FIL *)NULL;
    FRESULT   result = FR_OK;
    int       vol;
    los_part *part = (los_part *)NULL;

    if (!filep || !filep->f_inode || !filep->f_inode->i_private)
        return -EINVAL;

    fat = (FATFS *)filep->f_inode->i_private;
    vol = fatfs_get_vol(fat);
    if (vol < 0 || vol > _VOLUMES)
        return -ENOENT;

    fp = (FIL *)(filep->f_priv);
    FAT_CHECK(fp);

    result = f_close(fp);
    if (result == FR_OK)
    {
        /* close succeed, release memory */
        if(fp->buf != NULL)
            free((void *)fp->buf);
        free((void *)fp);
    }

#ifdef LOSCFG_FS_FAT_CACHE
    if (filep->f_inode->mountflags != MS_NOSYNC && result == FR_OK)
    {
        part = get_part((int)fat->drv);
        if (part != NULL)
        {
            (void)sd_sync(part->disk_id);
        }
        else
            return -ENODEV;
    }
#endif

    return fatfs_2_vfs(result);
}

int fatfs_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
{
    return -ENOSYS;
}

ssize_t fatfs_read(struct file *filep, char *buffer, size_t buflen)
{
    FIL     *fp = (FIL *)NULL;
    FRESULT  result;
    unsigned  int byte_read;

    fp = (FIL *)(filep->f_priv);
    FAT_CHECK(fp);

    result = f_read(fp, buffer, buflen, &byte_read);
    /* file position should be updated */
    filep->f_pos  = fp->fptr;

    if (result == FR_OK)
        return byte_read;

    return fatfs_2_vfs(result);
}

ssize_t fatfs_write(struct file *filep, const char *buffer, size_t buflen)
{
    FIL     *fp = (FIL *)NULL;
    FRESULT  result;
    unsigned int byte_write;

    if(filep->f_inode->mountflags == MS_RDONLY)
    {
        return -EACCES;
    }
    fp = (FIL *)(filep->f_priv);
    FAT_CHECK(fp);

    result = f_write(fp, buffer, buflen, &byte_write);
    /* file position should be updated */
    filep->f_pos  = fp->fptr;
    if ( FR_OK != f_sync(fp))
        PRINT_ERR("fatfs_write f_sync error \n");
    if (result == FR_OK)
        return byte_write;

    return fatfs_2_vfs(result);
}

int fatfs_sync(struct file *filep)
{
    FATFS    *fat = (FATFS *)NULL;
    FIL      *fp = (FIL *)NULL;
    FRESULT   result;
    int       vol;
    los_part *part = (los_part *)NULL;

    if (!filep || !filep->f_inode || !filep->f_inode->i_private)
        return -EINVAL;

    if(filep->f_inode->mountflags == MS_RDONLY)
    {
        return -EACCES;
    }

    fat = (FATFS *)filep->f_inode->i_private;
    vol = fatfs_get_vol(fat);
    if (vol < 0 || vol > _VOLUMES)
        return -ENOENT;

    fp = (FIL *)(filep->f_priv);
    FAT_CHECK(fp);

    result = f_sync(fp);

#ifdef LOSCFG_FS_FAT_CACHE
    if (result == FR_OK)
    {
        part = get_part((int)fat->drv);
        if (part != NULL)
        {
            (void)sd_sync(part->disk_id);
        }
        else
            return -ENODEV;
    }
#endif

    return fatfs_2_vfs(result);
}
 int fatfs_dup(FAR const struct file *oldp, FAR struct file *newp)
{
    return -ENOSYS;
}

off_t fatfs_seek(struct file *filep, off_t offset, int whence)
{
    FRESULT result = FR_OK;
    FIL    *fp = (FIL *)NULL;

    fp = (FIL *)(filep->f_priv);
    FAT_CHECK(fp);

    switch (whence)
    {
        case SEEK_SET: /* The offset is set to offset bytes. */
            break;

        case SEEK_CUR: /* The offset is set to its current location plus
                      * offset bytes. */
            offset += (off_t)filep->f_pos;
            break;

        case SEEK_END: /* The offset is set to the size of the file plus
                      * offset bytes. */
            offset += fp->obj.objsize;
            break;

        default:
            return -EINVAL;
    }

    if (offset < 0)
    {
        return -EINVAL;
    }

    result = f_lseek(fp, offset);
    if (result == FR_OK)
    {
        /* return current position */
        filep->f_pos = (off_t)fp->fptr;
        return (off_t)fp->fptr;
    }

    return fatfs_2_vfs(result);
}

loff_t fatfs_seek64(struct file *filep, loff_t offset, int whence)
{
    FRESULT result = FR_OK;
    FIL    *fp = (FIL *)NULL;

    fp = (FIL *)(filep->f_priv);
    FAT_CHECK(fp);

    switch (whence)
    {
        case SEEK_SET: /* The offset is set to offset bytes. */
            break;

        case SEEK_CUR: /* The offset is set to its current location plus
                      * offset bytes. */
            offset += filep->f_pos;
            break;

        case SEEK_END: /* The offset is set to the size of the file plus
                      * offset bytes. */
            offset += (loff_t)fp->obj.objsize;
            break;

        default:
            return -EINVAL;
    }

    if (offset < 0)
    {
        return -EINVAL;
    }

    result = f_lseek(fp, offset);
    if (result == FR_OK)
    {
        /* return current position */
        filep->f_pos = (loff_t)fp->fptr;
        return (loff_t)fp->fptr;
    }

    return fatfs_2_vfs(result);

}


int fatfs_unlink(struct inode *mountpt, const char *relpath)
{
    FATFS    *fat = (FATFS *)NULL;
    FRESULT   result;
    int       ret,vol;
    char     *f_path = (char *)NULL;
    los_part *part = (los_part *)NULL;
    struct stat st;

    if (strlen(relpath) > MAX_LFNAME_LENTH - VOLUME_CHAR_LENGTH)
    {
        return -ENAMETOOLONG;
    }

    if(mountpt->mountflags == MS_RDONLY)
    {
        return -EACCES;
    }

    ret = fatfs_stat(mountpt, relpath, &st);
    if (ret != FR_OK)
        return ret;

    switch(st.st_mode & S_IFMT)
    {
        case S_IFREG:
            break;
        case S_IFDIR:
            return -EISDIR;
        default:
            /* unknown file type */
            return -1;
    }

    fat = (FATFS *)mountpt->i_private;
    vol = fatfs_get_vol(fat);
    if (vol < 0 || vol > _VOLUMES)
        return -ENOENT;

    f_path = (char*)malloc(MAX_LFNAME_LENTH);
    if (f_path == NULL)
        return -ENOMEM;

    ret = snprintf_s(f_path, MAX_LFNAME_LENTH, MAX_LFNAME_LENTH - 1, "%d:%s", vol, relpath);
    if (ret < 0)
    {
        free(f_path);
        return -EINVAL;
    }

    result = f_unlink(f_path);

    free((void *)f_path);

#ifdef LOSCFG_FS_FAT_CACHE
    if (mountpt->mountflags != MS_NOSYNC && result == FR_OK)
    {
        part = get_part((int)fat->drv);
        if (part != NULL)
        {
            (void)sd_sync(part->disk_id);
        }
        else
            return -ENODEV;
    }
#endif

    return fatfs_2_vfs(result);
}


int fatfs_rename(struct inode *mountpt, const char *oldpath, const char *newpath)
{
    FATFS      *fat = (FATFS *)NULL;
    FRESULT     result;
    char       *oldfspath = (char *)NULL;
    const char *newfspath = (const char *)NULL;
    los_part   *part = (los_part *)NULL;
    int         vol;
    size_t      oldlen, newlen, len;
    int         ret;

    if (strlen(oldpath) > MAX_LFNAME_LENTH - VOLUME_CHAR_LENGTH)
    {
        return -ENAMETOOLONG;
    }

    if(mountpt->mountflags == MS_RDONLY)
    {
        return -EACCES;
    }

    oldlen = strlen(oldpath);
    newlen = strlen(newpath);
    if (!oldlen || !newlen)
        return -EPERM;

    len = oldlen < newlen ? oldlen : newlen;
    if (!strncmp(oldpath, newpath, len) && (oldpath[len] == '/' || newpath[len] == '/'))
        return -EPERM;

    fat = (FATFS *)mountpt->i_private;
    vol = fatfs_get_vol(fat);
    if (vol < 0 || vol > _VOLUMES)
        return -ENOENT;

    oldfspath = (char*)malloc(MAX_LFNAME_LENTH);
    if (oldfspath == NULL)
        return -ENOMEM;

    newfspath = newpath;
    ret = snprintf_s(oldfspath, MAX_LFNAME_LENTH, MAX_LFNAME_LENTH - 1, "%d:%s", vol, oldpath);
    if (ret < 0)
    {
        free(oldfspath);
        return -EINVAL;
    }

    result = f_rename(oldfspath, newfspath);

    free((void *)oldfspath);

#ifdef LOSCFG_FS_FAT_CACHE
    if (mountpt->mountflags != MS_NOSYNC && result == FR_OK)
    {
        part = get_part((int)fat->drv);
        if (part != NULL)
        {
            (void)sd_sync(part->disk_id);
        }
        else
            return -ENODEV;
    }
#endif

    return fatfs_2_vfs(result);
}
void get_stmtime(struct stat *st, FILINFO *finfo)
{
    struct tm ftm;
    int year, mon, day, hour, min, sec;
    WORD mtime;

    mtime = finfo->fdate;
    day = mtime & 0x1F;           /* bit[4:0] Day(1..31) */
    mtime >>= 5;
    mon = mtime & 0x0F;           /* bit[8:5] Month(1..12) */
    mtime >>= 4;
    year = (mtime & 0x7F) + 1980; /* bit[15:9] Year since 1980(0..127) */

    mtime = finfo->ftime;
    sec = (mtime & 0x1F) * 2;     /* bit[4:0] Second/2(0..29) */
    mtime >>= 5;
    min = mtime & 0x3F;           /* bit[10:5] Minute(0..59) */
    mtime >>= 6;
    hour = mtime & 0x1F;          /* bit[15:11] Hour(0..23) */

    (void)memset_s(&ftm, sizeof(ftm), 0, sizeof(ftm));
    ftm.tm_year = year - 1900; /* Years since 1900 */
    ftm.tm_mon  = mon - 1;     /* Months since January: 0-11 */
    ftm.tm_mday = day;         /* Day: 1-31 */
    ftm.tm_hour = hour;        /* Hours: 0-23 */
    ftm.tm_min  = min;         /* Minutes: 0-59 */
    ftm.tm_sec  = sec;         /* Seconds: 0-59 */

   st->st_mtime = mktime(&ftm);
}

int fatfs_stat(struct inode *mountpt, const char *path, struct stat *buf)
{
    FILINFO finfo;
    FRESULT result;
    FATFS  *fat = (FATFS *)NULL;
    int     vol;
    char   *f_path = (char *)NULL;
    int    ret;

    if (!mountpt || !mountpt->i_private)
        return -EINVAL;

    if (strlen(path) > MAX_LFNAME_LENTH - VOLUME_CHAR_LENGTH)
    {
        return -ENAMETOOLONG;
    }

    fat = (FATFS *)mountpt->i_private;
    vol = fatfs_get_vol(fat);
    if (vol < 0 || vol > _VOLUMES)
        return -ENOENT;

    f_path = (char*)malloc(MAX_LFNAME_LENTH);
    if (f_path == NULL)
        return -ENOMEM;

    ret = snprintf_s(f_path, MAX_LFNAME_LENTH, MAX_LFNAME_LENTH - 1, "%d:%s", vol, path);
    if (ret < 0)
    {
        free(f_path);
        return -EINVAL;
    }

    result = f_stat(f_path, &finfo);

    (void)memset_s((void *)buf, sizeof(struct stat), 0, sizeof(struct stat));

    if (result == FR_OK)
    {
        buf->st_dev = 0;

        buf->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH |
                       S_IWUSR | S_IWGRP | S_IWOTH;

        if (finfo.fattrib & AM_RDO)
            buf->st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);

        if (finfo.fattrib & AM_DIR)
        {
            buf->st_mode &= ~S_IFREG;
            buf->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
        }


        buf->st_size    = (long long) finfo.fsize;
#if _MAX_SS != _MIN_SS
        buf->st_blksize = (unsigned long)fat->ssize * fat->csize;
#else
        buf->st_blksize = (unsigned long) 512 * fat->csize;
#endif
        buf->st_blocks  = (unsigned long long) (buf->st_size + buf->st_blksize - 1) / buf->st_blksize;
        get_stmtime(buf, &finfo);
    }

    if (result == FR_INVALID_NAME) //root directoy
    {
        buf->st_dev   = 0;
        buf->st_mode  = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH |
                        S_IWUSR | S_IWGRP | S_IWOTH ;

        buf->st_mode &= ~S_IFREG;
        buf->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
        buf->st_mtime = 0;
        buf->st_size  = 0;
#if _MAX_SS != _MIN_SS
        buf->st_blksize = (unsigned long)fat->ssize * fat->csize;
#else
        buf->st_blksize = (unsigned long)512 * fat->csize;
#endif
        buf->st_blocks  = (unsigned long long) (buf->st_size + buf->st_blksize - 1) / buf->st_blksize;
        result = FR_OK;
    }

    free((void *)f_path);

    return fatfs_2_vfs(result);
}

int fatfs_stat64(struct inode *mountpt, const char *path, struct stat64 *buf)
{
    FILINFO finfo;
    FRESULT result;
    FATFS  *fat = (FATFS *)NULL;
    int     vol;
    char   *f_path = (char *)NULL;
    int    ret;

    if (!mountpt || !mountpt->i_private)
        return -EINVAL;

    if (strlen(path) > MAX_LFNAME_LENTH - VOLUME_CHAR_LENGTH)
    {
        return -ENAMETOOLONG;
    }

    fat = (FATFS *)mountpt->i_private;
    vol = fatfs_get_vol(fat);
    if (vol < 0 || vol > _VOLUMES)
        return -ENOENT;

    f_path = (char*)malloc(MAX_LFNAME_LENTH);
    if (f_path == NULL)
        return -ENOMEM;

    ret = snprintf_s(f_path, MAX_LFNAME_LENTH, MAX_LFNAME_LENTH - 1, "%d:%s", vol, path);
    if (ret < 0)
    {
        free(f_path);
        return -EINVAL;
    }

    result = f_stat(f_path, &finfo);

    (void)memset_s((void *)buf, sizeof(struct stat64), 0, sizeof(struct stat64));

    if (result == FR_OK)
    {
        buf->st_dev = 0;

        buf->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH |
                       S_IWUSR | S_IWGRP | S_IWOTH;

        if (finfo.fattrib & AM_RDO)
            buf->st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);

        if (finfo.fattrib & AM_DIR)
        {
            buf->st_mode &= ~S_IFREG;
            buf->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
        }

        buf->st_size    = (long long) finfo.fsize;
#if _MAX_SS != _MIN_SS
        buf->st_blksize = (unsigned long)fat->ssize * fat->csize;
#else
        buf->st_blksize = (unsigned long) 512 * fat->csize;
#endif
        buf->st_blocks  = (unsigned long long) (buf->st_size + buf->st_blksize - 1) / buf->st_blksize;
        get_stmtime((struct stat *)buf, &finfo);
    }

    if (result == FR_INVALID_NAME) //root directoy
    {
        buf->st_dev   = 0;
        buf->st_mode  = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH |
                        S_IWUSR | S_IWGRP | S_IWOTH ;

        buf->st_mode &= ~S_IFREG;
        buf->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
        buf->st_mtime = 0;
        buf->st_size  = 0;
#if _MAX_SS != _MIN_SS
        buf->st_blksize = (unsigned long)fat->ssize * fat->csize;
#else
        buf->st_blksize = (unsigned long)512 * fat->csize;
#endif
        buf->st_blocks  = (unsigned long long) (buf->st_size + buf->st_blksize - 1) / buf->st_blksize;
        result = FR_OK;
    }

    free((void *)f_path);

    return fatfs_2_vfs(result);

}

int fatfs_statfs(struct inode *mountpt, struct statfs *buf)
{
    char    drive[4];
    DWORD   fre_clust;
    FATFS  *fat = (FATFS *)NULL;
    FRESULT result;
    int     vol;
    int     ret;

    FAT_CHECK(mountpt);
    FAT_CHECK(buf);

    fat = (FATFS *)mountpt->i_private;
    FAT_CHECK(fat);
    vol = fatfs_get_vol(fat);
    if (vol < 0 || vol > _VOLUMES)
        return -ENOENT;

    ret = snprintf_s(drive, sizeof(drive), sizeof(drive) - 1, "%d:", vol);
    if (ret < 0)
    {
        return -EINVAL;
    }

    result = f_getfree(drive, &fre_clust, &fat);
    if (result != FR_OK)
        return fatfs_2_vfs(result);

    (void)memset_s((void *)buf, sizeof(struct statfs), 0, sizeof(struct statfs));
    buf->f_type   = MSDOS_SUPER_MAGIC;
    buf->f_bfree  = fre_clust;
    buf->f_bavail = fre_clust;
    buf->f_blocks = fat->n_fatent - 2;
#if _MAX_SS != _MIN_SS
    buf->f_bsize  = fat->ssize * fat->csize;
#else
    buf->f_bsize  = 512 * fat->csize;
#endif

#if _USE_LFN
    buf->f_namelen = _MAX_LFN;        /* Maximum length of filenames */
#else
    buf->f_namelen = (8+1+3);        /* Maximum length of filenames */
#endif

    return 0;
}

int fatfs_utime(struct inode *mountpt, const char *pathname, const struct tm * set_tm)
{
#if _USE_CHMOD
    FILINFO finfo;
    FRESULT result;
    FATFS  *fat = (FATFS *)NULL;
    int     vol;
    int     status;
    char   *f_path = (char *)NULL;

    if (!mountpt || !mountpt->i_private)
        return -EINVAL;

    if (strlen(pathname) > MAX_LFNAME_LENTH - VOLUME_CHAR_LENGTH)
    {
        return -ENAMETOOLONG;
    }

    if (set_tm->tm_year < 80)
    {
        PRINT_ERR("modetime must be lager than 1980-1-1\n");
        return -EINVAL;
    }

    if(mountpt->mountflags == MS_RDONLY)
    {
        return -EACCES;
    }
    fat = (FATFS *)mountpt->i_private;
    vol = fatfs_get_vol(fat);
    if (vol < 0 || vol > _VOLUMES)
        return -ENOENT;

    f_path = (char*)malloc(MAX_LFNAME_LENTH);
    if (f_path == NULL)
        return -ENOMEM;

    status = snprintf_s(f_path, MAX_LFNAME_LENTH, MAX_LFNAME_LENTH - 1, "%d:%s", vol, pathname);
    if (status < 0)
    {
        free(f_path);
        return -EINVAL;
    }

    /* transform set_tm to be the format what we want,
     * and write in fatfs through f_utime to set modified
     * time.
     */
    finfo.fdate = (WORD)(((set_tm->tm_year - 80) << 9) | (set_tm->tm_mon + 1) << 5 | set_tm->tm_mday);
    finfo.ftime = (WORD)(set_tm->tm_hour << 11 | set_tm->tm_min << 5 | set_tm->tm_sec >> 1);
    result = f_utime(f_path, &finfo);
    status = fatfs_2_vfs(result);
    free(f_path);
    return status;
#else
    return -ENOSYS;
#endif
}

int fatfs_opendir(struct inode *mountpt, const char *relpath, struct fs_dirent_s *dir)
{
    char   *f_path = (char *)NULL;
    DIR    *f_dir = (DIR *)NULL;
    FATFS  *fat = (FATFS *)NULL;
    FRESULT result;
    int     vol = 0;
    int     ret = 0;

    if (strlen(relpath) > MAX_LFNAME_LENTH - VOLUME_CHAR_LENGTH)
    {
        return -ENAMETOOLONG;
    }

    FAT_CHECK(dir);
    fat = (FATFS *)mountpt->i_private;
    FAT_CHECK(fat);
    vol = fatfs_get_vol(fat);
    if (vol < 0 || vol > _VOLUMES)
        return -ENOENT;

    f_path = (char*)malloc(MAX_LFNAME_LENTH);
    if (f_path == NULL)
        return -ENOMEM;

    ret = snprintf_s(f_path, MAX_LFNAME_LENTH, MAX_LFNAME_LENTH - 1, "%d:%s", vol, relpath);
    if (ret < 0)
    {
        free(f_path);
        return -EINVAL;
    }

    /* open directory */
    f_dir = (DIR *)malloc(sizeof(DIR));
    if (f_dir == NULL)
    {
        free((void *)f_path);
        return -ENOMEM;
    }

    result = f_opendir(f_dir, f_path);
    free((void *)f_path);

    if (result != FR_OK)
    {
        free((void *)f_dir);
        return fatfs_2_vfs(result);
    }

    dir->u.fs_dir = (fs_dir_s)f_dir;
    return FR_OK;

}

int fatfs_closedir(struct inode *mountpt, struct fs_dirent_s *dir)
{
    DIR    *f_dir = (DIR *)NULL;
    FRESULT result;

    FAT_CHECK(dir);
    f_dir = (DIR *)(dir->u.fs_dir);
    FAT_CHECK(f_dir);

    result = f_closedir(f_dir);
    if (result == FR_OK)
    {
        free(f_dir);
        return FR_OK;
    }

    return fatfs_2_vfs(result);
}

int fatfs_readdir(struct inode *mountpt, struct fs_dirent_s *dir)
{
    DIR    *f_dir = (DIR *)NULL;
    FILINFO fno;
    FRESULT result;
    struct dirent *d = (struct dirent *)NULL;
    unsigned int min_size;
    unsigned int dst_name_size;

    FAT_CHECK(dir);
    f_dir = (DIR *)(dir->u.fs_dir);
    d = &dir->fd_dir;

    (void)memset_s(&fno, sizeof(FILINFO), 0, sizeof(FILINFO));
    result = f_readdir(f_dir, &fno);
    if (result != FR_OK)
    {
        return fatfs_2_vfs(result);
    }

    /*0x00:Reached end of directory.
     *0xFF:The directory is empty.
     */
    if (fno.fname[0] == 0 || fno.fname[0] == (TCHAR)0xFF)
    {
        return -ENOENT;
    }

    if (fno.fattrib & AM_DIR)
        d->d_type = DT_DIR;
    else
        d->d_type = DT_REG;

    // d->d_namlen = (uint8_t)strlen(fn); //_DIRENT_HAVE_D_NAMLEN
    d->d_reclen = (uint16_t)sizeof(struct dirent);
    dst_name_size = sizeof(d->d_name);
    min_size = dst_name_size < sizeof(fno.fname) ? dst_name_size : sizeof(fno.fname);
    (void)strncpy_s(d->d_name, dst_name_size, fno.fname, min_size);
    d->d_name[dst_name_size - 1] = '\0';
    d->d_off = dir->fd_position;

    return ENOERR;
}

int fatfs_readdir_all(DIR_FAT *dir_fat)
{
    char   *fn = (char *)NULL;
    DIR    *f_dir = (DIR *)NULL;
    FILINFO fno;
    FRESULT result;
    struct fs_dirent_s *dir = (struct fs_dirent_s *)NULL;
    struct fat_direntall *d = (struct fat_direntall *)NULL;
    int ret;

    FAT_CHECK(dir_fat);
    dir = (struct fs_dirent_s *)dir_fat->stDirStream.dp;

    FAT_CHECK(dir);
    f_dir = (DIR *)(dir->u.fs_dir);
    d = &dir_fat->stBuf.direntall;

    result = f_readdir(f_dir, &fno);
    if (result != FR_OK)
    {
        return fatfs_2_vfs(result);
    }
    /*0x00:Reached end of directory.
     *0xFF:The directory is empty.
     */
    if (fno.fname[0] == 0 || fno.fname[0] == (TCHAR)0xFF)
    {
        return -ENOENT;
    }

    fn = fno.fname;

    if (fno.fattrib & AM_DIR)
        d->d_type = DT_DIR;
    else
        d->d_type = DT_REG;

#ifdef _DIRENT_HAVE_D_NAMLEN
    d->d_namlen = (uint8_t)strlen(fn);
#endif
    d->d_reclen = (uint16_t) (sizeof(struct fat_direntall) + strlen(fn));
    ret = strncpy_s(d->d_name, sizeof(d->d_name) + PATH_MAX, fn, strlen(fn));
    if (ret != EOK)
    {
        return -ENAMETOOLONG;
    }
#ifdef _DIRENT_HAVE_D_OFF
    d->d_off++;
#endif


    d->d_size = fno.fsize;

    /* get st_mtime. */
    {
        //struct tm ftm;
        int year, mon, day, hour, min, sec;
        WORD tmp;

        tmp = fno.fdate;
        day = tmp & 0x1F;           /* bit[4:0] Day(1..31) */
        tmp >>= 5;
        mon = tmp & 0x0F;           /* bit[8:5] Month(1..12) */
        tmp >>= 4;
        year = tmp & 0x7F;          /* bit[15:9] Year since 1980(0..127) */

        tmp = fno.ftime;
        sec = (tmp & 0x1F) * 2;     /* bit[4:0] Second/2(0..29) */
        tmp >>= 5;
        min = tmp & 0x3F;           /* bit[10:5] Minute(0..59) */
        tmp >>= 6;
        hour = tmp & 0x1F;          /* bit[15:11] Hour(0..23) */

        d->d_createtime[0] = year;
        d->d_createtime[1] = mon;
        d->d_createtime[2] = day;
        d->d_createtime[3] = hour;
        d->d_createtime[4] = min;
        d->d_createtime[5] = sec;
    }

    return ENOERR;
}

int fatfs_rewinddir(struct inode *mountpt, struct fs_dirent_s *dir)
{
    DIR    *f_dir = (DIR *)NULL;
    FRESULT result;

    FAT_CHECK(dir);
    f_dir = (DIR *)(dir->u.fs_dir);
    FAT_CHECK(f_dir);

    result = f_rewinddir(f_dir);
    if (result != FR_OK)
    {
        return fatfs_2_vfs(result);
    }

    return FR_OK;
}

int fatfs_mkdir(struct inode *mountpt, const char *relpath, mode_t mode)
{
    char     *f_path = (char *)NULL;
    FATFS    *fat = (FATFS *)NULL;
    FRESULT   result;
    int       vol;
    los_part *part = (los_part *)NULL;
    int      ret = 0;

    if(mountpt->mountflags == MS_RDONLY)
    {
        return -EACCES;
    }

    if (strlen(relpath) > MAX_LFNAME_LENTH - VOLUME_CHAR_LENGTH)
        return -ENAMETOOLONG;

    fat = (FATFS *)mountpt->i_private;
    FAT_CHECK(fat);
    vol = fatfs_get_vol(fat);
    if (vol < 0 || vol > _VOLUMES)
        return -ENOENT;

    f_path = (char*)malloc(MAX_LFNAME_LENTH);
    if (f_path == NULL)
        return -ENOMEM;

    ret = snprintf_s(f_path, MAX_LFNAME_LENTH, MAX_LFNAME_LENTH - 1, "%d:%s", vol, relpath);
    if (ret < 0)
    {
        free(f_path);
        return -EINVAL;
    }

    result = f_mkdir(f_path);
    free((void *)f_path);

#ifdef LOSCFG_FS_FAT_CACHE
    if (mountpt->mountflags != MS_NOSYNC && result == FR_OK)
    {
        part = get_part((int)fat->drv);
        if (part != NULL)
        {
            (void)sd_sync(part->disk_id);
        }
        else
            return -ENODEV;
    }
#endif

    return fatfs_2_vfs(result);
}

int fatfs_rmdir(struct inode *mountpt, const char *relpath)
{
    char       *f_path = (char *)NULL;
    FATFS      *fat = (FATFS *)NULL;
    FRESULT     result;
    int         ret,vol;
    los_part   *part = (los_part *)NULL;
    struct stat st;

    if (strlen(relpath) > MAX_LFNAME_LENTH - VOLUME_CHAR_LENGTH)
    {
        return -ENAMETOOLONG;
    }

    if(mountpt->mountflags == MS_RDONLY)
    {
        return -EACCES;
    }

    ret = fatfs_stat(mountpt, relpath, &st);
    if (ret != FR_OK)
        return ret;

    switch(st.st_mode & S_IFMT)
    {
        case S_IFDIR:
            break;
        case S_IFREG:
            return -ENOTDIR;
        default:
            /* unknown file type */
            return -1;
    }

    fat = (FATFS *)mountpt->i_private;
    FAT_CHECK(fat);
    vol = fatfs_get_vol(fat);
    if (vol < 0 || vol > _VOLUMES)
        return -ENOENT;

    f_path = (char*)malloc(MAX_LFNAME_LENTH);
    if (f_path == NULL)
        return -ENOMEM;

    ret = snprintf_s(f_path, MAX_LFNAME_LENTH, MAX_LFNAME_LENTH - 1, "%d:%s", vol, relpath);
    if (ret < 0)
    {
        free(f_path);
        return -EINVAL;
    }

    result = f_unlink(f_path);
    free((void *)f_path);

#ifdef LOSCFG_FS_FAT_CACHE
    if (mountpt->mountflags != MS_NOSYNC && result == FR_OK)
    {
        part = get_part((int)fat->drv);
        if (part != NULL)
        {
            (void)sd_sync(part->disk_id);
        }
        else
            return -ENODEV;
    }
#endif

    return fatfs_2_vfs(result);
}

/*  return  : 0 : OK; -ENODEV : "/dev/mmc0" driver not ready
 *  sectors : 0~128 0:auto seclect; 1:512Byte; 2:1KB; 4 : 2KB; 128:64KB; if secotrs > 128,auto seclect;
 */

int fatfs_mkfs(const char *dev, int sectors, int option)
{
    int       result;
    FATFS     fat       = {};
    unsigned int flag   = 0;
    int       index     = 0;
    int       err       = ENOERR;
#ifdef LOSCFG_FS_FAT_VIRTUAL_PARTITION
    int       vir_ret   = 0;
#endif
    los_part *part      = NULL;
    char      drive[8]  = {};
    char*     work = NULL;
#if _USE_LABEL
    char      label[15] = {};
#endif
    struct inode *mmc_node = NULL;
    int erase_flag = 0;
    char *mbr_buf = NULL;

    fat.win = NULL;
    if (option & FMT_ERASE)
    {
        option &= ~FMT_ERASE;
        erase_flag = 1;
    }
    if (option != FM_FAT && option != FM_FAT32)
    {
        option = FM_ANY;
    }


    //if device not register in system,it may cause serious error,so return -EIO
    mmc_node = inode_find(dev, 0);
    if (mmc_node == NULL)
        return -ENODEV; //"mmmc0" driver not ready


    part = los_part_find(mmc_node);
    if (part == NULL || part->dev == NULL)
    {
        inode_release(mmc_node);
        return -ENODEV;
    }

    if (erase_flag == 1)
    {
        err = los_disk_erase(part->disk_id, part->sector_start, part->sector_count);
        if (err != 0)
        {
            PRINTK("SD card erase error.\n");
        }
    }

    index = fatfs_get_disk(mmc_node);
    if (index == -1) //if not mount,find an empty fatfs to mount;
    {
        index = fatfs_get_vol((FATFS *)NULL);
        if (index == -1)
        {
            err = -EEXIST;
            goto errout;//can't find empty fatfs;
        }

        VolToPart[index].di = part->disk_id;
        VolToPart[index].pd = part->part_id;
        VolToPart[index].pt = part->part_no_mbr;
        VolToPart[index].ps = part->sector_start;
        VolToPart[index].pc = part->sector_count;

        result = snprintf_s(drive, sizeof(drive), sizeof(drive) - 1, "%d:/", index);
        if (result < 0)
        {
            err = -EINVAL;
            goto errout;
        }

        result = f_mount(&fat, drive, 0);
        if (result != FR_OK)
        {
            err = fatfs_2_vfs(result);
            goto errout;
        }

        disk_dev[index] = mmc_node;
        flag |= 1<<1;
    }
    else
    {
        result = snprintf_s(drive, sizeof(drive), sizeof(drive) - 1, "%d:/", index);
        if (result < 0)
        {
            inode_release(mmc_node);
            return -EINVAL;
        }

        flag = 0;
    }
    work = (char*)malloc(_MAX_SS);
    if (work == NULL)
    {
        err = -ENOMEM;
        goto errout_with_fatfs;
    }

    result = f_mkfs(drive, option, sectors, work, _MAX_SS);
    free(work);
    if (result != FR_OK)
    {
        err = fatfs_2_vfs(result);
        goto errout_with_fatfs;
    }
#ifdef LOSCFG_FS_FAT_VIRTUAL_PARTITION
    if(flag != 0)
    {
        vir_ret = VIRERR_NOTMOUNT;
    }
    else
    {
        if (mmc_node != inode_find(FatVirPart.VirPartInfo.devpartpath, (const char **)NULL))
        {
            vir_ret = VIRERR_NOPARAM;
        }
        else
        {
            if(option != FM_FAT32)
            {
                vir_ret = VIRERR_NOTFIT;
                (void)fatfs_unbind_vir_part(f_getfatfs(index));
            }
            else
            {
                (void)fatfs_unbind_vir_part(f_getfatfs(index));
                vir_ret = fatfs_make_vir_part(f_getfatfs(index), index);
                if (vir_ret < VIRERR_BASE)
                    vir_ret = FR_OK;
            }
        }
    }
#endif
#if _USE_LABEL
    result = snprintf_s(label, sizeof(label), sizeof(label) - 1, "%d:%s", index, g_fat_label);
    if (result < 0)
    {
        err = -EINVAL;
        goto errout_with_fatfs;
    }

    result = f_setlabel(label);
    if (result != FR_OK)
    {
        dprintf("Set sd card volume label failed !\n ");
    }
#endif

#ifdef LOSCFG_FS_FAT_CACHE
    if (result == FR_OK)
    {
        err = sd_sync(part->disk_id);
        if(err < 0)
        {
            err = -EIO;
            goto errout_with_fatfs;
        }
    }
#endif

    //if there is no mbr before, the partition info needs to be changed after formatting.
    if ((part->type != EMMC) && (part->part_no_mbr == 0))
    {
        los_disk *disk = get_disk(part->disk_id);
        if (disk == NULL)
        {
            err = -EIO;
            goto errout_with_fatfs;
        }
        mbr_buf = (char *)memalign(64, disk->sector_size);
        if (mbr_buf == NULL)
        {
            err = -ENOMEM;
            goto errout_with_fatfs;
        }
        (void)memset_s(mbr_buf, disk->sector_size, 0, disk->sector_size);
        result = los_disk_read(part->disk_id,mbr_buf,0, 1);
        if (result < 0)
        {
            err = -EIO;
            free(mbr_buf);
            goto errout_with_fatfs;
        }
        part->sector_start = LD_DWORD_DISK(&mbr_buf[PAR_OFFSET + PAR_START_OFFSET]);
        part->sector_count = LD_DWORD_DISK(&mbr_buf[PAR_OFFSET + PAR_COUNT_OFFSET]);
        part->part_no_mbr = 1;
        part->filesystem_type = mbr_buf[PAR_OFFSET + PAR_TYPE_OFFSET];

        VolToPart[index].pt = part->part_no_mbr;
        VolToPart[index].ps = part->sector_start;
        VolToPart[index].pc = part->sector_count;
        free(mbr_buf);
    }

errout_with_fatfs:
    if (flag & 1<<1)
    {
        (void)f_mount(NULL, drive, 0); //unmount the tmp fatfs
        disk_dev[index] = (struct inode *)NULL;
    }

errout:
    inode_release(mmc_node);
    if(fat.win != NULL)
        free(fat.win);
#ifdef LOSCFG_FS_FAT_VIRTUAL_PARTITION
    if(err == 0)
        err = vir_ret;
#endif
    return err;
}

int fatfs_chattr(struct inode *mountpt, const char *relpath, mode_t mode)
{
#if _USE_CHMOD
    FRESULT         result;
    FATFS           *fat = (FATFS *)NULL;
    int             vol;
    char            *f_path = (char *)NULL;
    unsigned char   attr;
    los_part        *part = (los_part *)NULL;
    int             status;

    if (!mountpt || !mountpt->i_private)
        return -EINVAL;

    if (strlen(relpath) > MAX_LFNAME_LENTH - VOLUME_CHAR_LENGTH)
    {
        return -ENAMETOOLONG;
    }

    if(mountpt->mountflags == MS_RDONLY)
    {
        return -EACCES;
    }
    attr = mode & (AM_RDO|AM_HID|AM_SYS|AM_ARC);

    fat = (FATFS *)mountpt->i_private;
    vol = fatfs_get_vol(fat);
    if (vol < 0 || vol > _VOLUMES)
        return -ENOENT;

    f_path = (char*)malloc(MAX_LFNAME_LENTH);
    if (f_path == NULL)
        return -ENOMEM;

    status = snprintf_s(f_path, MAX_LFNAME_LENTH, MAX_LFNAME_LENTH - 1, "%d:%s", vol, relpath);
    if (status < 0)
    {
        free(f_path);
        return -EINVAL;
    }

    result = f_chmod(f_path, attr, 0xff);

#ifdef LOSCFG_FS_FAT_CACHE
    if (mountpt->mountflags != MS_NOSYNC && result == FR_OK)
    {
        part = get_part((int)fat->drv);
        if (part != NULL)
        {
            (void)sd_sync(part->disk_id);
        }
        else
        {
            free(f_path);
            return -ENODEV;
        }
    }
#endif
    status = fatfs_2_vfs(result);

    free(f_path);
    return status;
#else
    return -ENOSYS;
#endif
}

int fatfs_getlabel(void *handle, char* label)
{
    BYTE    index;
    FATFS  *fat = (FATFS *)NULL;
    FRESULT result;
    los_part *part = (los_part *)NULL;
    char drive[8];
    DWORD *vsn = NULL;
    int   ret;

    if(label == NULL)
        return -EFAULT;

    fat = (FATFS *)handle;
    if (fat == NULL)
        return -EINVAL;

    index = (BYTE) fatfs_get_vol(fat);
    if (index >= _VOLUMES || (INT8)index < 0)
        return -EINVAL;

    part = los_part_find(disk_dev[index]);
    if (part == NULL)
       return -ENODEV;

    ret = snprintf_s(drive, sizeof(drive), sizeof(drive) - 1, "%d:/", index);
    if (ret < 0)
    {
        return -EINVAL;
    }

    result = f_getlabel(drive, label, vsn);
    if (result != FR_OK)
      return fatfs_2_vfs(result);

    return OK;
}

int fatfs_fallocate(FAR struct file *filep, int mode, off_t offset, off_t len)
{
#if _USE_EXPAND
    FIL    *fp = (FIL *)NULL;
    FRESULT  res;
    fp = (FIL *)(filep->f_priv);
    FAT_CHECK(fp);

    if ( len > INT_MAX || len <= 0 || offset > INT_MAX || offset < 0 ) /*lint !e685*/
        return -EINVAL;

    if (mode != FALLOC_FL_KEEP_SIZE)
        return -EINVAL;

    if(filep->f_inode->mountflags == MS_RDONLY)
    {
        return -EACCES;
    }

    res = f_expand(fp, (FSIZE_t)offset, (FSIZE_t)len, FALLOC_FL_KEEP_SIZE); /*lint !e571*/

    if (res == FR_OK)
    {
        res = f_sync(fp);
    }

    return fatfs_2_vfs(res);
#else
    return -ENOSYS;
#endif
}

int fatfs_fallocate64(FAR struct file *filep, int mode, off64_t offset, off64_t len)
{
#if _USE_EXPAND
    FIL    *fp = (FIL *)NULL;
    FRESULT  res;
    fp = (FIL *)(filep->f_priv);
    FAT_CHECK(fp);

    if ( len >= FAT32_MAXSZIE || len <= 0 || offset >= FAT32_MAXSZIE || offset < 0 ) /*lint !e685*/
        return -EINVAL;

    if (mode != FALLOC_FL_KEEP_SIZE)
        return -EINVAL;

    res = f_expand(fp, (FSIZE_t)offset, (FSIZE_t)len, FALLOC_FL_KEEP_SIZE); /*lint !e571*/

    if (res == FR_OK)
    {
        res = f_sync(fp);
    }

    return fatfs_2_vfs(res);
#else
    return -ENOSYS;
#endif
}

int los_set_systime_status(BOOL b_status)
{
    if (FAT_SYSTEM_TIME_ENABLE != b_status && FAT_SYSTEM_TIME_DISABLE != b_status)
        return -EINVAL;

    f_settimestatus(b_status);
    return FR_OK;
}

int fatfs_truncate(FAR struct file *filep, off_t length)
{
#if !_FS_READONLY
    FIL    *fp = (FIL *)NULL;
    FRESULT  res = FR_OK;
    UINT count;
    DWORD n, fclust;
#ifdef LOSCFG_FS_FAT_CACHE
    los_part *part = (los_part *)NULL;
    FATFS    *fat = (FATFS *)NULL;
#endif

    if(filep->f_inode->mountflags == MS_RDONLY)
    {
        return -EACCES;
    }

    fp = (FIL *)(filep->f_priv);
    FAT_CHECK(fp);

    if (length > INT_MAX || length < 0) /*lint !e685*/
        return -EINVAL;

    res = f_getclustinfo(fp, &fclust, &count);
    if(res != FR_OK)
        return fatfs_2_vfs(res);

    if(count == 0xFFFFFFFF)
        return -EPERM;

    n = (DWORD)(fp->obj.fs)->csize * SS(fp->obj.fs);  /* Cluster size */

    if (length > count * n) /*lint !e574*/
    {
        res = f_expand(fp, 0, (FSIZE_t)(length), FALLOC_FL_KEEP_SIZE); /*lint !e571*/
    }
    else if (length < n * count) /*lint !e574*/
    {
        res = f_truncate(fp, (FSIZE_t)length); /*lint !e571*/
    }

    fp->obj.objsize = length; /* Set file size to length */
    fp->flag |= 0x40; /*Set modified flag*/

    if (res == FR_OK)
    {
        res = f_sync(fp);
    }

#ifdef LOSCFG_FS_FAT_CACHE
    if (filep->f_inode->mountflags != MS_NOSYNC && res == FR_OK)
    {
        fat = (FATFS *)filep->f_inode->i_private;
        part = get_part((int)fat->drv);
        if (part != NULL)
        {
            (void)sd_sync(part->disk_id);
        }
        else
            return -ENODEV;
    }
#endif

    return fatfs_2_vfs(res);
#else
    return -ENOSYS;
#endif
}

int fatfs_truncate64(FAR struct file *filep, off64_t length)
{
#if !_FS_READONLY
    FIL    *fp = (FIL *)NULL;
    FRESULT  res = FR_OK;
    UINT count;
    DWORD n, fclust;
#ifdef LOSCFG_FS_FAT_CACHE
    los_part *part = (los_part *)NULL;
    FATFS    *fat = (FATFS *)NULL;
#endif

    fp = (FIL *)(filep->f_priv);
    FAT_CHECK(fp);

    if (length >= FAT32_MAXSZIE || length < 0) /*lint !e685*/
        return -EINVAL;

    res = f_getclustinfo(fp, &fclust, &count);
    if(res != FR_OK)
        return fatfs_2_vfs(res);

    if(count == 0xFFFFFFFF)
        return -EPERM;

    n = (DWORD)(fp->obj.fs)->csize * SS(fp->obj.fs);  /* Cluster size */

    if (length > (off64_t)count * n) /*lint !e574*/
    {
        res = f_expand(fp, 0, (FSIZE_t)(length), FALLOC_FL_KEEP_SIZE); /*lint !e571*/
    }
    else if (length < (off64_t)count * n) /*lint !e574*/
    {
        res = f_truncate(fp, (FSIZE_t)length); /*lint !e571*/
    }

    fp->obj.objsize = length; /* Set file size to length */
    fp->flag |= 0x40; /*Set modified flag*/

    if (res == FR_OK)
    {
        res = f_sync(fp);
    }

#ifdef LOSCFG_FS_FAT_CACHE
    if (res == FR_OK)
    {
        fat = (FATFS *)filep->f_inode->i_private;
        part = get_part((int)fat->drv);
        if (part != NULL)
        {
            (void)sd_sync(part->disk_id);
        }
        else
            return -ENODEV;
    }
#endif

    return fatfs_2_vfs(res);
#else
    return -ENOSYS;
#endif
}

static int fatfs_timemin(DIR_FILE dir_file[])
{
    int min = 0;
    DWORD tm_min = 0;

    MIN((dir_file[0].fno.fdate << 16 | dir_file[0].fno.ftime), (dir_file[1].fno.fdate << 16 | dir_file[1].fno.ftime), tm_min);
    MIN((dir_file[2].fno.fdate << 16 | dir_file[2].fno.ftime), tm_min, tm_min);/*lint !e574*/
    if (tm_min == (dir_file[0].fno.fdate << 16 | dir_file[0].fno.ftime))
    {
        min = 0;
    }
    else if (tm_min == (dir_file[1].fno.fdate << 16 | dir_file[1].fno.ftime))
    {
        min = 1;
    }
    else if (tm_min == (dir_file[2].fno.fdate << 16 | dir_file[2].fno.ftime))
    {
        min = 2;
    }

    return min;
}

int fatfs_fscheck(struct inode *mountpt, const char *relpath, struct fs_dirent_s *dir)
{
    DIR    *f_dir = (DIR *)NULL;
    FILINFO fno;
    FRESULT result;
    int count = 0, time_oldest = 0;
    DIR dir_backup = {0};
    DIR_FILE dir_file[3] = {0};
    int loop = 0;
#ifdef LOSCFG_FS_FAT_CACHE
    FATFS    *fat = (FATFS *)NULL;
    los_part   *part = (los_part *)NULL;
    int vol;
#endif

    if(mountpt->mountflags == MS_RDONLY)
    {
        return -EACCES;
    }
    FAT_CHECK(dir);

    result = fatfs_opendir(mountpt, relpath, dir);/*lint !e64*/
    if (result != FR_OK)
    {
        return result;
    }

    f_dir = (DIR *)(dir->u.fs_dir);
    if ((f_dir->obj.fs)->fs_type != FS_FAT32)
    {
        (void)fatfs_closedir(mountpt, dir);
        return -EINVAL;
    }

    do{
        (void)memcpy_s(&dir_backup, sizeof(DIR), f_dir, sizeof(DIR));
        result = f_readdir(f_dir, &fno);
        if (result != FR_OK)
        {
            break;
        }

        if ((fno.fname[0] == 0 || fno.fname[0] == (TCHAR)0xFF))
        {
            break;
        }

        if (AM_DIR == fno.fattrib)
            continue;

        if (0 == count)
        {
            (void)memcpy_s(&dir_file[0].f_dir, sizeof(DIR), &dir_backup, sizeof(DIR));
            (void)memcpy_s(&dir_file[0].fno, sizeof(FILINFO), &fno, sizeof(FILINFO));
        }
        else if (1 == count)
        {
            (void)memcpy_s(&dir_file[1].f_dir, sizeof(DIR), &dir_backup, sizeof(DIR));
            (void)memcpy_s(&dir_file[1].fno, sizeof(FILINFO), &fno, sizeof(FILINFO));
        }
        else if (2 == count)
        {
            (void)memcpy_s(&dir_file[2].f_dir, sizeof(DIR), &dir_backup, sizeof(DIR));
            (void)memcpy_s(&dir_file[2].fno, sizeof(FILINFO), &fno, sizeof(FILINFO));
            time_oldest = fatfs_timemin(dir_file);
        }
        else
        {
            if ((fno.fdate << 16 | fno.ftime) > (dir_file[time_oldest].fno.fdate << 16 | dir_file[time_oldest].fno.ftime))
            {
                (void)memcpy_s(&dir_file[time_oldest].f_dir, sizeof(DIR), &dir_backup, sizeof(DIR));
                (void)memcpy_s(&dir_file[time_oldest].fno, sizeof(FILINFO), &fno, sizeof(FILINFO));
                time_oldest = fatfs_timemin(dir_file);
            }
        }
        count ++;
    }while(FR_OK == result);
    if(count >=3)
        loop = 3;
    else
        loop = count;
    result = fatfs_closedir(mountpt, dir);/*lint !e64*/

    if (result != FR_OK)
    {
        return result;
    }

    for (count = 0; count < loop; count++)
    {
        result = f_fcheckfat(&dir_file[count]);
        if (result != FR_OK)
        {
            return fatfs_2_vfs(result);
        }
    }

#ifdef LOSCFG_FS_FAT_CACHE
    fat = (FATFS *)mountpt->i_private;
    vol = fatfs_get_vol(fat);
    if (vol < 0 || vol > _VOLUMES)
        return -ENOENT;
    part = get_part((int)fat->drv);
    if (part != NULL)
    {
        (void)sd_sync(part->disk_id);
    }
#endif

    return ENOERR;
}

int fatfs_virstatfs(struct inode *mountpt, const char* relpath, struct statfs *buf){
#ifdef LOSCFG_FS_FAT_VIRTUAL_PARTITION
    return fatfs_virstatfs_internel(mountpt, relpath, buf);
#else
    return 0;
#endif
}

const struct mountpt_operations fat_operations =
{
    fatfs_open,          /* open */
    fatfs_close,         /* close */
    fatfs_read,          /* read */
    fatfs_write,         /* write */
    fatfs_seek,          /* seek */
    fatfs_ioctl,         /* ioctl */

    fatfs_sync,          /* sync */
    fatfs_dup,           /* dup */

    fatfs_opendir,       /* opendir */
    fatfs_closedir,      /* closedir */
    fatfs_readdir,       /* readdir */
    fatfs_rewinddir,     /* rewinddir */

    fatfs_bind,          /* bind */
    fatfs_unbind,        /* unbind */
    fatfs_statfs,        /* statfs */
    fatfs_virstatfs,     /* virstatfs */
    fatfs_unlink,        /* unlinke */
    fatfs_mkdir,         /* mkdir */
    fatfs_rmdir,         /* rmdir */
    fatfs_rename,        /* rename */
    fatfs_stat,          /* stat */
    fatfs_stat64,        /* stat64 */
    fatfs_utime,         /* utime */
    fatfs_chattr,        /* chattr*/
    fatfs_seek64,        /* seek64 */
    fatfs_getlabel,      /* getlabel */
    fatfs_fallocate,     /* fallocate */
    fatfs_fallocate64,   /* fallocate64 */
    fatfs_truncate,      /* truncate */
    fatfs_truncate64,    /* truncate64 */
    fatfs_fscheck        /* fscheck */
};

FSMAP_ENTRY(fat_fsmap, "vfat", fat_operations, FALSE, TRUE); /*lint !e19*/


#ifdef __cplusplus
#if __cplusplus
}
#endif /* __cplusplus */
#endif /* __cplusplus */

#endif /* #ifdef CONFIG_FS_FAT */
